package com.pingan.haofang.searchcloud.common.validator;

import com.pingan.haofang.framework.common.functional.lang.control.validator.bo.ValidationError;
import com.pingan.haofang.framework.common.functional.lang.control.validator.bo.ValidationResult;
import com.pingan.haofang.searchcloud.common.exception.FieldException;
import com.pingan.haofang.searchcloud.common.exception.FieldExceptionItem;
import com.pingan.haofang.searchcloud.common.validator.annotation.Valid;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.hibernate.validator.HibernateValidator;
import org.springframework.stereotype.Component;

import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Locale;


/**
 * 参数校验切面
 *
 * @author BAOJIE371
 * @date 2017/2/20
 * @since 1.0.0
 */
@Component
@Aspect
public class ValidAspect {

    private static final Validator FAILFAST_VALIDATOR;
    private static final Validator FAILOVER_VALIDATOR;

    // 初始化2个Hibernate Validator，分别适用于failfast和failover场景
    static {
        Locale.setDefault(Locale.SIMPLIFIED_CHINESE);
        FAILFAST_VALIDATOR = Validation.byProvider(HibernateValidator.class)
                .configure()
                .failFast(true)
                .buildValidatorFactory()
                .getValidator();
        FAILOVER_VALIDATOR = Validation.byProvider(HibernateValidator.class)
                .configure()
                .failFast(false)
                .buildValidatorFactory()
                .getValidator();
    }

    /**
     * 函数执行前执行参数校验
     *
     * @param point
     * @param valid
     */
    @Before(value = "@annotation(valid)")
    public void beforeExecution(JoinPoint point, Valid valid) {
        Object[] arguments = point.getArgs();
        boolean failFast = valid.failFast();
        boolean hibernateValidate = valid.hibernateValidate();
        int hibernateErrorCode = valid.hibernateErrorCode();
        Class<?> value = valid.value();
        String handlerName = valid.method().isEmpty() ? point.getSignature().getName() : valid.method();
        ValidationResult result = ValidationResult.build();
        if (hibernateValidate) {
            // 执行Hibernate Validator校验
            javax.validation.Validator hibernateValidator = failFast ? FAILFAST_VALIDATOR : FAILOVER_VALIDATOR;
            for (Object argument : arguments) {
                result.addErrors(hibernateValidator.validate(argument).stream()
                        .map(v -> ValidationError.of(v.getPropertyPath().toString(), v.getMessage())
                                .invalidValue(v.getInvalidValue())
                                .errorCode(hibernateErrorCode)));

                if (failFast && !result.isSuccess()) {
                    throwFieldException(result);
                }
            }

            // 根据提供的校验器进行校验
            if (!value.equals(Void.class)) {
                ValidHandlerBean handler = ValidHandlerFactory.getHandler(handlerName, value)
                        .orElseThrow(() -> new RuntimeException("validHandler not found"));
                Object[] newArguments = new Object[arguments.length + 1];
                newArguments[0] = result;
                System.arraycopy(arguments, 0, newArguments, 1, arguments.length);
                handler.invoke(newArguments);
            }

            if (!result.isSuccess()) {
                throwFieldException(result);
            }
        }
    }

    private void throwFieldException(ValidationResult result) {
        throw new FieldException(result.getGlobalErrorMessage(), result.getErrors().stream()
                .map(error -> new FieldExceptionItem(error.getField(), error.getErrorMsg(), error.getParams()))
                .toArray(FieldExceptionItem[]::new));
    }
}

